﻿/**************************************************************
 *
 * 唯一标识：f803d157-aa5b-42ba-9c63-0207374f925d
 * 命名空间：Sgr.Oss.Services
 * 创建时间：2023/8/25 9:49:15
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.Utilities;
using System.Buffers;

namespace Sgr.Oss.Services
{
    /// <summary>
    /// 基于本地文件系统的对象存储服务实现
    /// </summary>
    public class LocalFilesystemOssService : IOssService
    {
        #region 字段和构造函数

        /// <summary>
        /// 文件读写缓冲区大小（10KB）
        /// </summary>
        private const int BUFFER_SIZE = 10240;

        private readonly LocalFilesystemOptions _options;
        private readonly ILogger<LocalFilesystemOssService> _logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options">本地文件系统配置选项</param>
        /// <param name="logger">日志记录器</param>
        public LocalFilesystemOssService(
            IOptions<LocalFilesystemOptions> options,
            ILogger<LocalFilesystemOssService> logger)
        {
            _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        #endregion 字段和构造函数

        #region 工具方法

        /// <summary>
        /// 验证对象名称是否合规
        /// </summary>
        /// <param name="name">对象名称</param>
        /// <returns>true 表示合规，false 表示不合规</returns>
        protected virtual bool IsValidObjectName(string name)
        {
            if (string.IsNullOrEmpty(name))
                return false;

            if (name.StartsWith('.'))
            {
                _logger.LogWarning("对象名称不能以'.'开头: {Name}", name);
                return false;
            }

            // 获取除斜杠外的所有非法字符

            var invalidChars = Path.GetInvalidFileNameChars()
                //.Where(c => c != Path.DirectorySeparatorChar)
                //.Where(c => c != '/' && c != '\\')
                .ToArray();

            if (name.IndexOfAny(invalidChars) >= 0)
            {
                _logger.LogWarning("对象名称包含非法字符: {Name}", name);
                return false;
            }

            // 额外的安全检查，防止路径遍历攻击
            if (name.Contains(".."))
            {
                _logger.LogWarning("对象名称包含非法序列'..'：{Name}", name);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 获取工作目录的完整路径
        /// </summary>
        /// <returns>工作目录的完整路径</returns>
        protected virtual string GetWorkingDirectory()
        {
            string workDir = _options.IsRelativePath
                ? Path.Combine(AppContext.BaseDirectory, _options.WorkDir)
                : _options.WorkDir;

            if (!Directory.Exists(workDir))
                Directory.CreateDirectory(workDir);

            return workDir;
        }

        /// <summary>
        /// 根据对象名称生成存储路径
        /// </summary>
        /// <param name="name">对象名称</param>
        /// <returns>对象的存储路径</returns>
        protected virtual string GetObjectPath(string name)
        {
            // 分区目录（不含工作目录/桶目录）
            string partitionPath = GetObjectPartitionPath(name);

            // 规范化：统一分隔符 -> '/'，去掉首尾多余的分隔符，防止出现空目录层级
            string normalized = name.Replace('\\', '/').Trim('/');

            // 再转换为当前平台分隔符，形成真正的层级目录结构
            string relativePath = normalized.Replace('/', Path.DirectorySeparatorChar);

            // 最终路径 = 分区目录 +（可能的多层子目录）+ 文件名（全部小写）
            return Path.Combine(partitionPath, relativePath.ToLowerInvariant());
        }

        /// <summary>
        /// 获取对象在存储桶中的完整路径
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">对象名称</param>
        /// <returns>对象的完整存储路径</returns>
        protected virtual string GetObjectFullPath(string bucketName, string objectName)
        {
            return Path.Combine(
                GetBucketPartitionPath(bucketName),
                bucketName,
                GetObjectPath(objectName));
        }

        /// <summary>
        /// 获取存储桶的分区路径
        /// </summary>
        /// <param name="bucketName"></param>
        /// <returns></returns>
        protected virtual string GetBucketPartitionPath(string bucketName)
        {
            // 使用哈希算法计算分区
            int partition = Math.Abs(bucketName.GetHashCode()) % _options.BucketPartitionCount;
            return Path.Combine(GetWorkingDirectory(), $"b{partition:D3}");
        }

        /// <summary>
        /// 获取对象的分区路径
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        protected virtual string GetObjectPartitionPath(string objectName)
        {
            // 计算对象分区
            int partition = Math.Abs(objectName.GetHashCode()) % _options.ObjectPartitionCount;
            return $"o{partition:D3}";
        }

        /// <summary>
        /// 检查目录文件数量限制
        /// </summary>
        /// <param name="directoryPath"></param>
        /// <returns>如果文件数量未超过限制，返回 true；否则返回 false</returns>
        protected virtual bool CheckDirectoryLimit(string directoryPath)
        {
            try
            {
                var fileCount = Directory.GetFiles(directoryPath, "*", SearchOption.TopDirectoryOnly)
                    .Count(f => !f.EndsWith(".metadata", StringComparison.OrdinalIgnoreCase));

                if (fileCount >= _options.MaxFilesPerDirectory)
                {
                    _logger.LogWarning(
                        "目录 {Directory} 中的文件数量 ({Count}) 已达到限制 ({Limit})",
                        directoryPath, fileCount, _options.MaxFilesPerDirectory);
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查目录 {Directory} 文件数量限制时发生错误", directoryPath);
                return false;
            }
        }

        /// <summary>
        /// 确保目录存在
        /// </summary>
        /// <param name="path">文件路径</param>
        protected virtual void EnsureDirectoryExists(string path)
        {
            string? directory = Path.GetDirectoryName(path);
            if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
        }

        /// <summary>
        /// 获取对象元数据文件的路径
        /// </summary>
        protected virtual string GetMetadataFilePath(string bucketName, string objectName)
        {
            string objectPath = GetObjectFullPath(bucketName, objectName);
            return objectPath + ".metadata";
        }

        /// <summary>
        /// 保存元数据
        /// </summary>
        protected virtual async Task SaveMetadataAsync(
            string metadataPath,
            IDictionary<string, string>? metadata,
            string contentType,
            CancellationToken cancellationToken = default)
        {
            var allMetadata = new Dictionary<string, string>();

            // 添加系统元数据
            if (!string.IsNullOrEmpty(contentType))
                allMetadata["content-type"] = contentType;

            // 添加用户自定义元数据
            if (metadata != null)
            {
                foreach (var pair in metadata)
                {
                    // 规范化元数据键名
                    var key = pair.Key.ToLowerInvariant();
                    if (!key.StartsWith("x-sgr-meta-"))
                        key = "x-sgr-meta-" + key;

                    allMetadata[key] = pair.Value;
                }
            }

            // 保存元数据到文件
            if (allMetadata.Count > 0)
            {
                //var json = System.Text.Json.JsonSerializer.Serialize(allMetadata);
                var json = JsonHelper.SerializeObject(allMetadata);
                await File.WriteAllTextAsync(metadataPath, json, cancellationToken);
            }
            else if (File.Exists(metadataPath))
            {
                File.Delete(metadataPath);
            }
        }

        /// <summary>
        /// 加载元数据
        /// </summary>
        protected virtual async Task<IDictionary<string, string>> LoadMetadataAsync(
            string metadataPath,
            CancellationToken cancellationToken = default)
        {
            if (!File.Exists(metadataPath))
                return new Dictionary<string, string>();

            var json = await File.ReadAllTextAsync(metadataPath, cancellationToken);
            if (string.IsNullOrEmpty(json))
                return new Dictionary<string, string>();

            //return System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json)
            //       ?? new Dictionary<string, string>();

            return JsonHelper.DeserializeObject<Dictionary<string, string>>(json)
                   ?? new Dictionary<string, string>();
        }

        /// <summary>
        /// 根据文件扩展名获取 Content-Type
        /// </summary>
        private static string GetContentType(string extension)
        {
            // 这里可以实现更完整的 MIME 类型映射
            return extension.ToLowerInvariant() switch
            {
                ".txt" => "text/plain",
                ".html" => "text/html",
                ".htm" => "text/html",
                ".jpg" => "image/jpeg",
                ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".gif" => "image/gif",
                ".pdf" => "application/pdf",
                ".doc" => "application/msword",
                ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ".xls" => "application/vnd.ms-excel",
                ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                _ => "application/octet-stream"
            };
        }

        /// <summary>
        /// 生成 ETag
        /// </summary>
        private static string GenerateETag(FileInfo fileInfo)
        {
            // 使用文件大小和最后修改时间生成简单的 ETag
            return $"\"{fileInfo.Length:X8}-{fileInfo.LastWriteTimeUtc.Ticks:X16}\"";
        }

        #endregion 工具方法

        #region 存储桶操作

        /// <summary>
        /// 检查存储桶是否存在
        /// </summary>
        public virtual Task<bool> BucketAnyAsync(string bucketName, CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

            if (!IsValidObjectName(bucketName))
                return Task.FromResult(false);

            string bucketPath = Path.Combine(GetBucketPartitionPath(bucketName), bucketName);
            bool exists = Directory.Exists(bucketPath);

            if (!exists)
                _logger.LogWarning("存储桶不存在: {BucketName}", bucketName);

            return Task.FromResult(exists);
        }

        /// <summary>
        /// 创建存储桶
        /// </summary>
        public virtual Task<bool> CreateBucketAsync(string bucketName, BucketAcl acl = BucketAcl.Private, CancellationToken cancellationToken = default)
        {
            try
            {
                Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

                if (!IsValidObjectName(bucketName))
                {
                    _logger.LogError("存储桶名称不合规: {BucketName}", bucketName);
                    return Task.FromResult(false);
                }

                string bucketPath = Path.Combine(GetBucketPartitionPath(bucketName), bucketName);
                if (!Directory.Exists(bucketPath))
                {
                    Directory.CreateDirectory(bucketPath);
                    _logger.LogInformation("成功创建存储桶: {BucketName}, ACL: {Acl}", bucketName, acl);
                }

                return Task.FromResult(true);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "创建存储桶失败: {BucketName}", bucketName);
                return Task.FromResult(false);
            }
        }

        /// <summary>
        /// 删除存储桶
        /// </summary>
        public virtual async Task<bool> RemoveBucketAsync(string bucketName, CancellationToken cancellationToken = default)
        {
            try
            {
                Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

                if (!await BucketAnyAsync(bucketName, cancellationToken))
                    return false;

                string bucketPath = Path.Combine(GetBucketPartitionPath(bucketName), bucketName);
                // 检查存储桶是否为空
                if (Directory.EnumerateFileSystemEntries(bucketPath).Any())
                {
                    _logger.LogError("无法删除非空存储桶: {BucketName}", bucketName);
                    return false;
                }

                Directory.Delete(bucketPath, true);
                _logger.LogInformation("成功删除存储桶: {BucketName}", bucketName);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除存储桶失败: {BucketName}", bucketName);
                return false;
            }
        }

        /// <summary>
        /// 获取所有存储桶名称
        /// </summary>
        public virtual Task<IEnumerable<string>> GetBucketNamesAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                string workDir = GetWorkingDirectory();
                if (!Directory.Exists(workDir))
                    return Task.FromResult(Enumerable.Empty<string>());

                var bucketNames = new List<string>();

                // 遍历所有分区目录
                for (int i = 0; i < _options.BucketPartitionCount; i++)
                {
                    string partitionDir = Path.Combine(workDir, $"b{i:D3}");
                    if (Directory.Exists(partitionDir))
                    {
                        var buckets = Directory
                            .GetDirectories(partitionDir)
                            .Select(path => Path.GetFileName(path))
                            .Where(name => !string.IsNullOrEmpty(name))
                            .ToList();

                        bucketNames.AddRange(buckets);
                    }
                }

                bucketNames = bucketNames.OrderBy(name => name).ToList();
                return Task.FromResult<IEnumerable<string>>(bucketNames);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取存储桶列表失败");
                return Task.FromResult(Enumerable.Empty<string>());
            }
        }

        #endregion 存储桶操作

        #region 存储对象操作

        /// <summary>
        /// 检查存储对象是否存在
        /// </summary>
        public virtual async Task<bool> ObjectsAnyAsync(string bucketName, string objectName, CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

            if (!IsValidObjectName(objectName))
                return false;

            // 检查存储桶是否存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
                return false;

            string filePath = GetObjectFullPath(bucketName, objectName);
            bool exists = File.Exists(filePath);

            if (!exists)
                _logger.LogWarning("对象不存在: Bucket={Bucket}, Object={Object}", bucketName, objectName);

            return exists;
        }

        /// <summary>
        /// 获取存储对象
        /// </summary>
        public virtual async Task GetObjectAsync(
            string bucketName,
            string objectName,
            Func<Stream, Task> callback,
            Action<ProgressInfo>? progress = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

            if (!IsValidObjectName(objectName))
                return;

            string filePath = GetObjectFullPath(bucketName, objectName);
            if (!File.Exists(filePath))
            {
                string message = $"要获取的对象不存在: Bucket={bucketName}, Object={objectName}";

                _logger.LogError(message);

                throw new FileNotFoundException(message);

                //_logger.LogError("要获取的对象不存在: Bucket={Bucket}, Object={Object}", bucketName, objectName);
                //return;
            }

            byte[] buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
            try
            {
                await using var fileStream = new FileStream(filePath,
                    FileMode.Open,
                    FileAccess.Read,
                    FileShare.None,
                    BUFFER_SIZE,
                    FileOptions.Asynchronous | FileOptions.SequentialScan);

                var progressInfo = new ProgressInfo
                {
                    TotalBytes = fileStream.Length,
                    TransferredBytes = 0
                };

                int bytesRead;
                while ((bytesRead = await fileStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken)) > 0)
                {
                    await using var ms = new MemoryStream(buffer, 0, bytesRead, writable: false);
                    await callback(ms);

                    progressInfo.TransferredBytes += bytesRead;
                    progress?.Invoke(progressInfo);
                }
            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffer);
            }
        }

        /// <summary>
        /// 获取存储对象并保存到文件
        /// </summary>
        public virtual async Task GetObjectAsync(
            string bucketName,
            string objectName,
            string filePath,
            Action<ProgressInfo>? progress = null,
            CancellationToken cancellationToken = default)
        {
            try
            {
                EnsureDirectoryExists(filePath);

                await using var fileStream = new FileStream(
                    filePath,
                    FileMode.Create,
                    FileAccess.Write,
                    FileShare.None,
                    BUFFER_SIZE,
                    FileOptions.Asynchronous | FileOptions.SequentialScan);

                await GetObjectAsync(
                    bucketName,
                    objectName,
                    async (stream) => await stream.CopyToAsync(fileStream, BUFFER_SIZE, cancellationToken),
                    progress,
                    cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "保存对象到文件失败: Bucket={Bucket}, Object={Object}, Path={FilePath}",
                    bucketName, objectName, filePath);
                throw;
            }
        }

        /// <summary>
        /// 上传对象
        /// </summary>
        public virtual async Task<bool> PutObjectAsync(
            string bucketName,
            string objectName,
            Stream data,
            IDictionary<string, string>? metadata = null,
            ObjectAcl acl = ObjectAcl.Inherited,
            Action<ProgressInfo>? progress = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

            if (!IsValidObjectName(objectName))
                return false;

            // 确保存储桶存在
            if (!await BucketAnyAsync(bucketName, cancellationToken))
            {
                if (!await CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                {
                    _logger.LogError("创建存储桶失败: {Bucket}", bucketName);
                    return false;
                }
            }

            try
            {
                // 保存文件内容
                string filePath = GetObjectFullPath(bucketName, objectName);

                // 确保目录存在
                EnsureDirectoryExists(filePath);

                // 检查目录文件数量限制
                string? directory = Path.GetDirectoryName(filePath);
                if (string.IsNullOrEmpty(directory) || !CheckDirectoryLimit(directory))
                    return false;

                var progressInfo = new ProgressInfo
                {
                    TotalBytes = data.CanSeek ? data.Length : -1,   // 如果流不可查找，设置总长度为-1
                    TransferredBytes = 0
                };

                const int progressInterval = 100; // 每100KB触发一次进度回调
                long lastProgressBytes = 0;

                byte[] buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
                try
                {
                    await using var fileStream = new FileStream(
                        filePath,
                        FileMode.Create,
                        FileAccess.Write,
                        FileShare.None,
                        BUFFER_SIZE,
                        FileOptions.Asynchronous | FileOptions.SequentialScan);

                    int bytesRead;
                    long totalBytesWritten = 0;

                    while ((bytesRead = await data.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken)) > 0)
                    {
                        await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
                        totalBytesWritten += bytesRead;

                        // 控制进度回调频率
                        if (progress != null && (totalBytesWritten - lastProgressBytes > progressInterval * 1024))
                        {
                            progressInfo.TransferredBytes = totalBytesWritten;
                            progress.Invoke(progressInfo);
                            lastProgressBytes = totalBytesWritten;
                        }
                    }

                    // 确保最后一次进度回调
                    if (progress != null && totalBytesWritten > lastProgressBytes)
                    {
                        progressInfo.TransferredBytes = totalBytesWritten;
                        // 更新总长度(对于不可查找的流)
                        if (progressInfo.TotalBytes == -1)
                            progressInfo.TotalBytes = totalBytesWritten;
                        progress.Invoke(progressInfo);
                    }

                    // 保存元数据
                    string metadataPath = GetMetadataFilePath(bucketName, objectName);
                    string contentType = GetContentType(Path.GetExtension(objectName));
                    await SaveMetadataAsync(metadataPath, metadata, contentType, cancellationToken);

                    return true;
                }
                finally
                {
                    ArrayPool<byte>.Shared.Return(buffer);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "上传对象失败: Bucket={Bucket}, Object={Object}", bucketName, objectName);
                return false;
            }
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        public virtual async Task<bool> PutObjectAsync(
          string bucketName,
          string objectName,
          string filePath,
          IDictionary<string, string>? metadata = null,
          ObjectAcl acl = ObjectAcl.Inherited,
          Action<ProgressInfo>? progress = null,
          CancellationToken cancellationToken = default)
        {
            // 参数验证
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
            Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");
            Check.StringNotNullOrWhiteSpace(filePath, nameof(filePath), "文件路径不能为空");

            if (!File.Exists(filePath))
            {
                _logger.LogError("要上传的文件不存在: {FilePath}", filePath);
                return false;
            }

            await using var fileStream = new FileStream(
                filePath,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read,
                BUFFER_SIZE,
                FileOptions.Asynchronous | FileOptions.SequentialScan);

            return await PutObjectAsync(
                bucketName,
                objectName,
                fileStream,
                metadata,
                acl,
                progress,
                cancellationToken);
        }

        /// <summary>
        /// 复制对象
        /// </summary>
        public virtual async Task<bool> CopyObjectAsync(
            string sourceBucketName,
            string sourceObjectName,
            string destBucketName,
            string? destObjectName = null,
            ObjectAcl acl = ObjectAcl.Inherited,
            CancellationToken cancellationToken = default)
        {
            if (!await ObjectsAnyAsync(sourceBucketName, sourceObjectName, cancellationToken))
            {
                _logger.LogError("源对象不存在: Bucket={Bucket}, Object={Object}",
                    sourceBucketName, sourceObjectName);
                return false;
            }

            destObjectName ??= sourceObjectName;

            if (!IsValidObjectName(destObjectName))
                return false;

            string sourceFilePath = GetObjectFullPath(sourceBucketName, sourceObjectName);
            string destFilePath = GetObjectFullPath(destBucketName, destObjectName);

            try
            {
                // 确保目标目录存在
                EnsureDirectoryExists(destFilePath);

                // 复制文件内容
                await using (var sourceStream = new FileStream(
                      sourceFilePath,
                      FileMode.Open,
                      FileAccess.Read,
                      FileShare.Read,
                      BUFFER_SIZE,
                      FileOptions.Asynchronous | FileOptions.SequentialScan))
                await using (var destStream = new FileStream(
                    destFilePath,
                    FileMode.Create,
                    FileAccess.Write,
                    FileShare.None,
                    BUFFER_SIZE,
                    FileOptions.Asynchronous | FileOptions.SequentialScan))
                {
                    await sourceStream.CopyToAsync(destStream, BUFFER_SIZE, cancellationToken);
                }

                // 复制元数据
                string sourceMetadataPath = GetMetadataFilePath(sourceBucketName, sourceObjectName);
                if (File.Exists(sourceMetadataPath))
                {
                    string destMetadataPath = GetMetadataFilePath(destBucketName, destObjectName);
                    File.Copy(sourceMetadataPath, destMetadataPath, true);
                }

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "复制对象失败: Source={Source}, Dest={Dest}",
                    $"{sourceBucketName}/{sourceObjectName}",
                    $"{destBucketName}/{destObjectName}");
                return false;
            }
        }

        /// <summary>
        /// 删除对象
        /// </summary>
        public virtual async Task<bool> RemoveObjectAsync(
            string bucketName,
            string objectName,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (!await ObjectsAnyAsync(bucketName, objectName, cancellationToken))
                    return false;

                string filePath = GetObjectFullPath(bucketName, objectName);
                string metadataPath = GetMetadataFilePath(bucketName, objectName);

                // 删除文件和元数据
                if (File.Exists(filePath))
                {
                    // 验证文件可访问性
                    await using (var fileStream = new FileStream(
                        filePath,
                        FileMode.Open,
                        FileAccess.ReadWrite,
                        FileShare.Delete,
                        BUFFER_SIZE,
                        FileOptions.Asynchronous))
                    {
                        // 当 await using 块结束时，fileStream 会自动释放
                    }

                    File.Delete(filePath);
                    _logger.LogInformation("成功删除文件: {FilePath}", filePath);
                }

                // 删除元数据文件
                if (File.Exists(metadataPath))
                {
                    File.Delete(metadataPath);
                    _logger.LogInformation("成功删除元数据文件: {MetadataPath}", metadataPath);
                }

                // 尝试删除空目录
                string? directory = Path.GetDirectoryName(filePath);
                while (!string.IsNullOrEmpty(directory) &&
                       !directory.EndsWith(bucketName) &&
                       Directory.Exists(directory) &&
                       !Directory.EnumerateFileSystemEntries(directory).Any())
                {
                    Directory.Delete(directory);
                    _logger.LogDebug("删除空目录: {Directory}", directory);
                    directory = Path.GetDirectoryName(directory);
                }

                _logger.LogInformation("成功删除对象: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                return true;
            }
            catch (IOException ex)
            {
                _logger.LogError(ex, "删除对象失败(IO错误): Bucket={Bucket}, Object={Object}, Message={Message}",
                    bucketName, objectName, ex.Message);
                return false;
            }
            catch (UnauthorizedAccessException ex)
            {
                _logger.LogError(ex, "删除对象失败(访问被拒绝): Bucket={Bucket}, Object={Object}, Message={Message}",
                    bucketName, objectName, ex.Message);
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除对象失败(其他错误): Bucket={Bucket}, Object={Object}, Message={Message}",
                    bucketName, objectName, ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 批量删除对象
        /// </summary>
        public virtual async Task<bool> RemoveObjectsAsync(
            string bucketName,
            IEnumerable<string> objectNames,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
                Check.NotNull(objectNames, nameof(objectNames), "对象名称集合不能为空");

                var namesList = objectNames.ToList();
                if (namesList.Count == 0)
                    return true;

                // 限制并发数
                var maxConcurrency = Math.Min(namesList.Count, 10);
                using var semaphore = new SemaphoreSlim(maxConcurrency);

                var tasks = namesList.Select(async name =>
                {
                    await semaphore.WaitAsync(cancellationToken);
                    try
                    {
                        return await RemoveObjectAsync(bucketName, name, cancellationToken);
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                });

                var results = await Task.WhenAll(tasks);

                // 记录删除结果
                var successCount = results.Count(x => x);
                var failureCount = results.Length - successCount;

                _logger.LogInformation(
                    "批量删除对象完成: Bucket={Bucket}, Total={Total}, Success={Success}, Failed={Failed}",
                    bucketName, results.Length, successCount, failureCount);

                return failureCount == 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "批量删除对象失败: Bucket={Bucket}", bucketName);
                return false;
            }
        }

        /// <summary>
        /// 获取对象元数据
        /// </summary>
        public virtual async Task<ObjectMetadata> GetObjectMetadataAsync(
            string bucketName,
            string objectName,
            CancellationToken cancellationToken = default)
        {
            try
            {
                // 参数验证
                Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
                Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

                // 检查对象是否存在
                if (!await ObjectsAnyAsync(bucketName, objectName, cancellationToken))
                {
                    var message = $"对象不存在: Bucket={bucketName}, Object={objectName}";
                    _logger.LogError(message);
                    throw new FileNotFoundException(message);
                }

                string filePath = GetObjectFullPath(bucketName, objectName);
                string metadataPath = GetMetadataFilePath(bucketName, objectName);

                // 验证文件访问权限
                var fileInfo = new FileInfo(filePath);
                if (!fileInfo.Exists)
                {
                    throw new FileNotFoundException($"文件不存在: {filePath}");
                }

                // 加载元数据
                var storedMetadata = await LoadMetadataAsync(metadataPath, cancellationToken);

                // 提取 Content-Type
                string contentType = storedMetadata.TryGetValue("content-type", out var ct)
                    ? ct
                    : GetContentType(fileInfo.Extension);

                // 提取用户自定义元数据
                var userMetadata = storedMetadata
                    .Where(pair => pair.Key.StartsWith("x-sgr-meta-"))
                    .ToDictionary(
                        pair => pair.Key["x-sgr-meta-".Length..],
                        pair => pair.Value,
                        StringComparer.OrdinalIgnoreCase);  // 使用不区分大小写的比较器

                var metadata = new ObjectMetadata
                {
                    Size = fileInfo.Length,
                    LastModified = fileInfo.LastWriteTimeUtc,  // 使用UTC时间
                    ContentType = contentType,
                    ETag = GenerateETag(fileInfo),
                    UserMetadata = userMetadata
                };

                _logger.LogDebug(
                    "获取对象元数据成功: Bucket={Bucket}, Object={Object}, Size={Size}, LastModified={LastModified}",
                    bucketName, objectName, metadata.Size, metadata.LastModified);

                return metadata;
            }
            catch (FileNotFoundException ex)
            {
                _logger.LogError(ex, "获取对象元数据失败(文件不存在): Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取对象元数据失败: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw;
            }
        }

        /// <summary>
        /// 列举对象
        /// </summary>
        public virtual async Task<ListObjectsResult> ListObjectsAsync(
            string bucketName,
            string? prefix = null,
            string? continuationToken = null,
            int maxKeys = 1000,
            bool useHierarchy = true,//当前类中该参数会被忽略，因为不存在子目录
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");

            string bucketPath = Path.Combine(GetBucketPartitionPath(bucketName), bucketName);

            var result = new ListObjectsResult
            {
                Objects = new List<ObjectInfo>(),
                CommonPrefixes = new List<string>(),
                IsTruncated = false,
                NextContinuationToken = null
            };

            try
            {
                var allFiles = Directory.GetFiles(bucketPath, "*.*", SearchOption.AllDirectories);
                var filteredFiles = allFiles
                    .Select(f =>
                    {
                        // 先获取相对路径并标准化分隔符
                        var relativePath = f[(bucketPath.Length + 1)..].Replace('\\', '/');
                        // 移除分区前缀和对象名称
                        var parts = relativePath.Split('/', 2);
                        // 如果有分区前缀（parts.Length > 1），返回实际对象路径，否则返回原路径
                        return (Path: parts.Length > 1 ? parts[1] : relativePath,
                                IsMetadata: relativePath.EndsWith(".metadata"));
                    })
                    .Where(f => !f.IsMetadata) // 过滤掉元数据文件
                    .Select(f => f.Path) // 只保留路径
                    .Where(f => string.IsNullOrEmpty(prefix) || f.StartsWith(prefix))
                    .OrderBy(f => f);

                // 处理延续标记
                if (!string.IsNullOrEmpty(continuationToken))
                {
                    //功能说明：
                    //    1.这是分页查询的一个实现机制，使用 continuationToken（延续标记）来标识从哪里继续获取下一页数据
                    //    2.continuationToken 通常是上一次查询结果中最后一个项目的标识符
                    //工作原理：
                    //    1.SkipWhile(f => f != continuationToken) - 跳过所有直到找到与 continuationToken 匹配的项
                    //    2.Skip(1) - 跳过匹配项本身（因为它已经在上一页中了）
                    //    3.OrderBy(f => f) - 确保结果保持有序

                    // 验证 continuationToken 是否在当前集合中
                    if (!filteredFiles.Contains(continuationToken))
                    {
                        _logger.LogWarning("无效的延续标记: {ContinuationToken}", continuationToken);
                        return new ListObjectsResult
                        {
                            Objects = new List<ObjectInfo>(),
                            CommonPrefixes = new List<string>()
                        };
                    }

                    filteredFiles = filteredFiles
                        .SkipWhile(f => f != continuationToken)
                        .Skip(1)
                        .OrderBy(f => f);
                }

                // 获取要返回的文件列表
                var files = filteredFiles.AsEnumerable().Take(maxKeys + 1).ToList();
                var hasMore = files.Count > maxKeys;
                if (hasMore)
                {
                    files = files.Take(maxKeys).ToList();
                    result.IsTruncated = true;
                    result.NextContinuationToken = files.Last();
                }

                // 构建对象列表
                var objects = new List<ObjectInfo>();
                foreach (var file in files)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    // 重新计算分区和完整路径
                    var partition = GetObjectPartitionPath(file);
                    var filePath = Path.Combine(bucketPath, partition, file);

                    if (!File.Exists(filePath))
                    {
                        _logger.LogWarning("在列举对象过程中，文件不存在: {FilePath}", filePath);
                        continue; // 跳过不存在的文件
                    }

                    try
                    {
                        var fileInfo = new FileInfo(filePath);
                        objects.Add(new ObjectInfo
                        {
                            ObjectName = file,
                            Size = fileInfo.Length,
                            LastModified = fileInfo.LastWriteTime,
                            ETag = GenerateETag(fileInfo)
                        });
                    }
                    catch (FileNotFoundException ex)
                    {
                        _logger.LogWarning(ex, "获取文件信息失败(文件不存在): {FilePath}", filePath);
                    }
                    catch (IOException ex)
                    {
                        _logger.LogWarning(ex, "获取文件信息失败(IO错误): {FilePath}", filePath);
                    }
                    catch (UnauthorizedAccessException ex)
                    {
                        _logger.LogWarning(ex, "获取文件信息失败(访问权限错误): {FilePath}", filePath);
                    }
                }

                // 处理公共前缀（模拟文件夹）
                if (!string.IsNullOrEmpty(prefix))
                {
                    var commonPrefixes = new HashSet<string>();
                    foreach (var file in files)
                    {
                        var relativePath = file[prefix.Length..];
                        var nextSlash = relativePath.IndexOf('/');
                        if (nextSlash >= 0)
                        {
                            commonPrefixes.Add(string.Concat(prefix, relativePath.AsSpan(0, nextSlash + 1)));
                        }
                    }
                    result.CommonPrefixes = commonPrefixes.OrderBy(p => p);
                }

                result.Objects = objects;
                return await Task.FromResult(result);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "列举对象失败: Bucket={Bucket}, Prefix={Prefix}", bucketName, prefix);
                throw;
            }
        }

        /// <summary>
        /// 获取对象访问URL
        /// </summary>
        public virtual async Task<string> GetPresignedGetUrlAsync(
            string bucketName,
            string objectName,
            TimeSpan expiry,
            CancellationToken cancellationToken = default)
        {
            // 验证对象是否存在
            if (!await ObjectsAnyAsync(bucketName, objectName, cancellationToken))
            {
                _logger.LogError("要生成访问URL的对象不存在: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw new FileNotFoundException($"对象不存在: Bucket={bucketName}, Object={objectName}");
            }

            try
            {
                // 计算过期时间戳
                var timestamp = DateTimeHelper.TimestampSeconds(expiry).ToString();

                // 构建URL路径
                var urlPath = $"/api/sgr/oss/local/{bucketName}/{objectName}".TrimEnd('/');

                // 生成签名
                var signature = GenerateGetSignature(bucketName, objectName, timestamp, this._options.SignKey);

                // 构建完整的URL
                var baseUrl = _options.BaseUrl.TrimEnd('/');
                var url = $"{baseUrl}{urlPath}?sig={signature}&exp={timestamp}";

                return url;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成预签名URL失败: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw;
            }
        }

        /// <summary>
        /// 获取用于上传的预签名URL
        /// </summary>
        public virtual async Task<string> GetPresignedPutUrlAsync(
            string bucketName,
            string objectName,
            TimeSpan expiry,
            string? contentType = null,
            IDictionary<string, string>? metadata = null,
            CancellationToken cancellationToken = default)
        {
            Check.StringNotNullOrWhiteSpace(bucketName, nameof(bucketName), "存储桶名称不能为空");
            Check.StringNotNullOrWhiteSpace(objectName, nameof(objectName), "对象名称不能为空");

            try
            {
                // 验证存储桶是否存在，如果不存在则创建
                if (!await BucketAnyAsync(bucketName, cancellationToken))
                {
                    if (!await CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                    {
                        _logger.LogError("创建存储桶失败: {Bucket}", bucketName);
                        throw new DirectoryNotFoundException($"存储桶不存在且无法创建: {bucketName}");
                    }
                }

                // 计算过期时间戳
                var timestamp = DateTimeHelper.TimestampSeconds(expiry).ToString();

                // 构建基础URL路径
                var urlPath = $"/api/sgr/oss/local/{bucketName}/{objectName}".TrimEnd('/');

                // 构建签名参数字典
                var parameters = new Dictionary<string, string>
                {
                    ["method"] = "PUT", // 指定HTTP方法为PUT
                    ["exp"] = timestamp
                };

                // 添加内容类型
                if (!string.IsNullOrEmpty(contentType))
                {
                    parameters["content-type"] = contentType;
                }

                // 添加元数据（如果有）
                if (metadata != null)
                {
                    foreach (var pair in metadata)
                    {
                        parameters[$"x-sgr-meta-{pair.Key.ToLowerInvariant()}"] = pair.Value;
                    }
                }

                // 生成签名（包含所有参数）
                string signature = GeneratePutSignature(bucketName, objectName, timestamp, _options.SignKey, parameters);

                // 构建完整的URL
                var baseUrl = _options.BaseUrl.TrimEnd('/');
                var queryString = string.Join("&",
                    parameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}")) +
                    $"&sig={signature}";

                var url = $"{baseUrl}{urlPath}?{queryString}";

                _logger.LogInformation(
                    "成功生成上传用预签名URL: Bucket={Bucket}, Object={Object}, ContentType={ContentType}, Expiry={Expiry}",
                    bucketName, objectName, contentType, expiry);

                return url;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成上传用预签名URL失败: Bucket={Bucket}, Object={Object}",
                    bucketName, objectName);
                throw;
            }
        }

        /// <summary>
        /// 生成 HTTP_PUT URL签名
        /// </summary>
        public static string GeneratePutSignature(string bucketName, string objectName, string timestamp, string signKey, Dictionary<string, string> parameters)
        {
            var signatureInput = string.Join("/",
                bucketName,
                objectName,
                timestamp,
                string.Join("&", parameters.OrderBy(p => p.Key).Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}")),
                signKey);

            var signature = HashHelper.CreateSha256(signatureInput).ToLowerInvariant();
            return signature;
        }

        /// <summary>
        /// 生成 HTTP_GET URL签名
        /// </summary>
        public static string GenerateGetSignature(string bucketName, string objectName, string timestamp, string signKey)
        {
            // 组合签名字符串
            var signStr = $"{bucketName}/{objectName}/{timestamp}/{signKey}";

            return HashHelper.CreateSha256(signStr)
                .ToLowerInvariant();
        }

        #endregion 存储对象操作
    }
}